/*
 * kernel/exception.c
 *
 * last modified by ChenJie at 2007.07.15
 *
 */

#include <asm/i8259.h>
#include <clinux/kernel.h>
#include <clinux/sched.h>
#include <clinux/mm.h>
#include <clinux/interrupt.h>
#include <cos/protect.h>

void exception00(void);
void exception01(void);
void exception02(void);
void exception03(void);
void exception04(void);
void exception05(void);
void exception06(void);
void exception07(void);
void exception08(void);
void exception09(void);
void exception10(void);
void exception11(void);
void exception12(void);
void exception13(void);
void exception14(void);
void exception15(void);
void exception16(void);

void hwint00(void);
void hwint01(void);
void hwint02(void);
void hwint03(void);
void hwint04(void);
void hwint05(void);
void hwint06(void);
void hwint07(void);
void hwint08(void);
void hwint09(void);
void hwint10(void);
void hwint11(void);
void hwint12(void);
void hwint13(void);
void hwint14(void);
void hwint15(void);

void system_call(void);

void unknown_interrupt(void);
static struct irq_struct irqs;

static void irqs_init()
{
	int i;
	// clear the irq_handle[]
	for(i=0; i<NR_IRQ; i++){
		irqs.irq_handle[i] = 0;
	}
	
	// clear the free_irq_hook
	irqs.free_irq_hook = 0;

	// add all the hooks to the free queue
	for(i=0; i<NR_IRQ * HOOKS_PER_IRQ; i++){
		// add the hook to the free-hook list:
		irqs.irq_hook[i].next = irqs.free_irq_hook;
		irqs.free_irq_hook = &irqs.irq_hook[i];
		// give every hook an id:
		irqs.irq_hook[i].id = i;
	}
}
static struct irq_hook * irqs_alloc_hook(void)
{
	struct irq_hook * p = irqs.free_irq_hook;
	//if there is a free hook:
	if(p == 0) return 0; // fail

	// get a hook from the free-list:
	irqs.free_irq_hook = p->next;

	// initalize the hook:
	p->next = 0;
	p->handler = 0;
	p->irq = 0;
	p->pid = 0;
	
	return p; //successful
}
static void irqs_free_hook(struct irq_hook * p)
{
	// check if the p is null:
	if(p==0) return;

	// add the hook to the free-list:
	p->next = irqs.free_irq_hook;
	irqs.free_irq_hook = p;
	
	return;
}

static void handle_irq(int irq)
{
	// check if the irq is in [0, 15]:
	if(irq<0 || irq > NR_IRQ) return;
	
	// visit every hook, and call the handler:
	struct irq_hook *p = irqs.irq_handle[irq];
	while(p!=0) {
		if(p->handler) (*p->handler)(p);
		p = p->next;
	}
}

long irq_put_hook(int irq, irq_handler handler, int pid)
{
	// check if the "irq" and "handler" is validate:
	if(irq < 0 || irq >NR_IRQ-1 || handler==0) return -1;

	// malloc a hook :
	struct irq_hook * p = irqs_alloc_hook();
	if(p==0) return -1;

	// initalize the new hook:
	p->handler = handler;
	p->irq = irq;
	p->pid = pid;

	// add the new hook to the irq-list:
	p->next = irqs.irq_handle[p->irq];
	irqs.irq_handle[p->irq] = p;

	// return the new hook's id:
	return p->id; // successful
}

long irq_rm_hook(long id)
{
	struct irq_hook *p;
	struct irq_hook ** last =0;
	
	// check the id is validate:
	if(id<0 || id > HOOKS_PER_IRQ * NR_IRQ) return -1;

	// get the target hook's pointer:
	p = &irqs.irq_hook[id];

	// remove the hook from the irq-list:
	last=&irqs.irq_handle[p->irq]; 
	while(*last != 0 && *last != p) last = &((*last)->next);
	if(*last==0) return -1;
	*last = p->next;
	p->next = 0;
	
	//add the hook to the free-list:
	irqs_free_hook(p);
	
	return 0; // successful
}


void do_interrupt(struct kstack_struct *kstack, int irq)
{
	procs.run_proc->reenter ++;

	handle_irq(irq);

	procs.run_proc->reenter --;
	return;
}

void interrupt_init(void)
{
	int i;
	// initalize the i8259 pic chip:
	i8259_init();
	
	// initalize the irqs:
	irqs_init();

	// initalize the idt:
	set_trap_gate(&idt[0], &exception00);
	set_syscall_gate(&idt[1], &exception01);
	set_trap_gate(&idt[2], &exception02);
	set_trap_gate(&idt[3], &exception03);
	set_trap_gate(&idt[4], &exception04);
	set_trap_gate(&idt[5], &exception05);
	set_trap_gate(&idt[6], &exception06);
	set_trap_gate(&idt[7], &exception07);
	set_trap_gate(&idt[8], &exception08);
	set_trap_gate(&idt[9], &exception09);
	set_trap_gate(&idt[10],&exception10);
	set_trap_gate(&idt[11],&exception11);
	set_trap_gate(&idt[12],&exception12);
	set_trap_gate(&idt[13],&exception13);
	set_trap_gate(&idt[14],&exception14);
	set_trap_gate(&idt[15],&exception15);
	set_trap_gate(&idt[16],&exception16);
	for(i=17; i<256;i++) {
		set_trap_gate(&idt[i], &unknown_interrupt);
	}
	set_intr_gate(&idt[IRQ_VECTOR( 0)], &hwint00);
	set_intr_gate(&idt[IRQ_VECTOR( 1)], &hwint01);
	set_intr_gate(&idt[IRQ_VECTOR( 2)], &hwint02);
	set_intr_gate(&idt[IRQ_VECTOR( 3)], &hwint03);
	set_intr_gate(&idt[IRQ_VECTOR( 4)], &hwint04);
	set_intr_gate(&idt[IRQ_VECTOR( 5)], &hwint05);
	set_intr_gate(&idt[IRQ_VECTOR( 6)], &hwint06);
	set_intr_gate(&idt[IRQ_VECTOR( 7)], &hwint07);
	set_intr_gate(&idt[IRQ_VECTOR( 8)], &hwint08);
	set_intr_gate(&idt[IRQ_VECTOR( 9)], &hwint09);
	set_intr_gate(&idt[IRQ_VECTOR(10)], &hwint10);
	set_intr_gate(&idt[IRQ_VECTOR(11)], &hwint11);
	set_intr_gate(&idt[IRQ_VECTOR(12)], &hwint12);
	set_intr_gate(&idt[IRQ_VECTOR(13)], &hwint13);
	set_intr_gate(&idt[IRQ_VECTOR(14)], &hwint14);
	set_intr_gate(&idt[IRQ_VECTOR(15)], &hwint15);
	set_syscall_gate(&idt[SYSCALL_VECTOR], &system_call);

}


